package http

import (
	"bytes"
	"strings"
	"text/template"
)

var httpTemplate = `
type {{.ServiceType}}Handler interface {
{{range .MethodSets}}
	{{.Name}}({{$.CtxPackage}}Context, *{{.Request}}) (*{{.Reply}}, error)
{{end}}
}

{{range .Methods}}
func local_handler_{{$.ServiceType}}_{{ .Name }}_{{.Num}} (svr interface{}, c *gin.Context) {
	var in {{.Request}}
{{if and (ne .Method "GET") (ne .Method "DELETE")}}
	if err := c.ShouldBind(&in); err != nil {
		{{$.TransportPackage}}Error(c, {{$.ErrorsPackage}}InvalidArgument(err.Error()).Err())
		return 
	}
{{end}}
	if err := {{$.TransportPackage}}MapProto(&in, c.Request.URL.Query()); err != nil {
		{{$.TransportPackage}}Error(c, {{$.ErrorsPackage}}InvalidArgument(err.Error()).Err())
		return 
	}
{{range  $param := .Vars}}
	var (
		val string
		ok bool
	)
	val, ok = c.Params.Get({{$param | printf "%q"}})
	if !ok {
		{{$.TransportPackage}}Error(c, {{$.ErrorsPackage}}InvalidArgument("not found {{$param}}").Err())
		return
	}
	if err := {{$.TransportPackage}}PopulateFieldFromPath(&in, {{$param | printf "%q"}}, val); err != nil {
		{{$.TransportPackage}}Error(c, {{$.ErrorsPackage}}InvalidArgument(err.Error()).Err())
		return
	}
{{end}}
	md := metadata.New(nil)
	for k, v := range c.Request.Header {
		md.Set(k, v...)
	}
	timeout := {{$.TimePackage}}Second * 0
	ctx := {{$.CtxPackage}}Background()
	if tm := c.GetHeader("Yggdrasil-Timeout"); tm != "" {
		var err error
		if timeout, err = {{$.TimePackage}}ParseDuration(tm); err == nil {
			var cancel {{$.CtxPackage}}CancelFunc
			ctx, cancel = {{$.CtxPackage}}WithTimeout(ctx, timeout)
			defer cancel()
		}
	}
	newCtx := metadata.WithInContext(ctx, md)
	c.Set("req", &in)
	out, err := svr.({{ $.ServiceType }}Handler).{{.Name}}(newCtx, &in)
	if err != nil {
		{{$.TransportPackage}}Error(c, err)
		return
	}
	c.Set("out", out)
	{{$.TransportPackage}}Success(c, out)
	return
}
{{end}}

var Local_{{.ServiceType}}_handlerDesc = {{.TransportPackage}}ServiceDesc{
	ServiceName: "{{.ServiceName}}",
	HandlerType: (*{{.ServiceType}}Handler)(nil),
	Methods: []{{.TransportPackage}}MethodDesc{
{{range .Methods}}{
			MethodType: "{{.Method}}",
			MethodName: "{{ .Name }}_{{.Num}}",
			Path: "{{.Path}}",
			Handler:	local_handler_{{$.ServiceType}}_{{ .Name }}_{{.Num}},
		},
{{end}}
	},
}
`

type serviceDesc struct {
	TransportPackage string
	ErrorsPackage    string
	TimePackage      string
	CtxPackage       string
	ServiceType      string // Greeter
	ServiceName      string // helloworld.Greeter
	Metadata         string // api/helloworld/helloworld.proto
	Methods          []*methodDesc
	MethodSets       map[string]*methodDesc
}

type methodDesc struct {
	// method
	Name    string
	Num     int
	Vars    []string
	Forms   []string
	Request string
	Reply   string
	// http_rule
	Path         string
	Method       string
	Body         string
	ResponseBody string
}

func (s *serviceDesc) execute() string {
	s.MethodSets = make(map[string]*methodDesc)
	for _, m := range s.Methods {
		s.MethodSets[m.Name] = m
	}
	buf := new(bytes.Buffer)
	tmpl, err := template.New("http").Parse(strings.TrimSpace(httpTemplate))
	if err != nil {
		panic(err)
	}
	if err := tmpl.Execute(buf, s); err != nil {
		panic(err)
	}
	return string(buf.Bytes())
}
